package com.sneaker.shower.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sneaker.shower.common.AES;
import com.sneaker.shower.common.BizException;
import com.sneaker.shower.entity.domain.BaseDomain;
import com.sneaker.shower.entity.domain.organization.OrganizationDO;
import com.sneaker.shower.entity.domain.sys.SysUserDO;
import com.sneaker.shower.entity.dto.SysUserDTO;
import com.sneaker.shower.entity.dto.UserDTO;
import com.sneaker.shower.mapper.SysUserMapper;
import com.sneaker.shower.mapstruct.SysUserMapStruct;
import com.sneaker.shower.service.OrganizationService;
import com.sneaker.shower.service.SysUserService;
import net.jodah.expiringmap.ExpirationPolicy;
import net.jodah.expiringmap.ExpiringMap;
import org.apache.commons.codec.digest.Md5Crypt;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUserDO> implements SysUserService {
    private final OrganizationService organizationService;
    private final TransactionTemplate transactionTemplate;
    private ExpiringMap<String, String> expiringMap;

    @Autowired
    public SysUserServiceImpl(OrganizationService organizationService,
                              TransactionTemplate transactionTemplate) {
        this.organizationService = organizationService;
        this.transactionTemplate = transactionTemplate;
    }

    @PostConstruct
    public void init() {
        expiringMap = ExpiringMap.builder().expiration(5, TimeUnit.DAYS).expirationPolicy(ExpirationPolicy.CREATED).build();
    }

    @Override
    public String invitationCode() {
        String random = RandomStringUtils.random(6, true, true).toUpperCase();
        expiringMap.put(random, random);
        return random;
    }

    @Override
    public UserDTO login(String userName, String password) {
        if (StringUtils.isBlank(userName) || StringUtils.isBlank(password)) {
            throw new IllegalArgumentException("请输入用户名密码");
        }
        SysUserDO sysUserDO = baseMapper.selectOne(new LambdaQueryWrapper<SysUserDO>().eq(SysUserDO::getUserName, userName).eq(SysUserDO::getPassword, Md5Crypt.md5Crypt(password.getBytes())));
        if (sysUserDO == null) {
            throw new BizException("用户名密码错误");
        }
        try {
            UserDTO dto = SysUserMapStruct.INSTANCE.do2DTO(sysUserDO, "sysUser");
            dto.setGuid(AES.encrypt(dto.getGuid()));
            dto.setTypeEnc(AES.encrypt(dto.getType()));
            dto.setSysUserGuid(sysUserDO.getGuid().toString());
            return dto;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BizException("系统繁忙，请稍候再试");
        }
    }

    @Override
    public UserDTO login(String openId) {
        if (StringUtils.isBlank(openId)) {
            throw new IllegalArgumentException("当前微信暂未绑定客服账号");
        }
        SysUserDO sysUserDO = baseMapper.selectOne(new LambdaQueryWrapper<SysUserDO>().eq(SysUserDO::getOpenId, openId));
        if (sysUserDO == null) {
            throw new BizException("用户不存在");
        }
        UserDTO dto = SysUserMapStruct.INSTANCE.do2DTO(sysUserDO, "sysUser");
        dto.setSysUserGuid(sysUserDO.getGuid().toString());
        dto.setTypeEnc(dto.getType());
        return dto;
    }

    @Override
    public Boolean isExist(String openId) {
        SysUserDO one = getOne(new LambdaQueryWrapper<SysUserDO>().eq(SysUserDO::getOpenId, openId));
        return one != null;
    }

    @Override
    public List<UserDTO> listAll() {
        List<SysUserDO> sysUserDOS = baseMapper.selectList(new LambdaQueryWrapper<SysUserDO>().eq(SysUserDO::getRole, 1));
        Map<Long, List<SysUserDO>> ref = sysUserDOS.stream().collect(Collectors.groupingBy(SysUserDO::getOrganizationGuid));
        List<OrganizationDO> organizationDOS = organizationService.list(new LambdaQueryWrapper<OrganizationDO>().select(BaseDomain::getGuid, OrganizationDO::getName).in(BaseDomain::getGuid, ref.keySet()));
        Map<Long, String> refs = organizationDOS.stream().collect(Collectors.toMap(OrganizationDO::getGuid, OrganizationDO::getName));
        return sysUserDOS.stream().map(SysUserMapStruct.INSTANCE::do2DTO).peek((s) ->
                s.setOrganizationName(
                        Optional.ofNullable(refs.get(Long.parseLong(s.getOrganizationGuid()))).orElse("--")
                )
        ).collect(Collectors.toList());
    }

    @Override
    public String addSysUser(SysUserDTO dto) {
        if (dto.getOrganizationGuid() == null) {
            throw new BizException("请选择一个门店");
        }
        return transactionTemplate.execute((transactionStatus) -> {
            try {
                Optional.ofNullable(expiringMap.get(dto.getInvitationCode().toUpperCase())).filter(StringUtils::isNoneBlank).orElseThrow(() -> new IllegalArgumentException("邀请码错误"));
                OrganizationDO organizationDO = Optional.ofNullable(organizationService.getById(dto.getOrganizationGuid())).orElseThrow(() -> new IllegalArgumentException("机构不存在"));
                SysUserDO sysUserDO = new SysUserDO()
                        .setNumber(organizationDO.getName() + "-" + getNumber(organizationDO.getGuid()))
                        .setOrganizationGuid(organizationDO.getGuid())
                        .setRole(dto.getRole())
                        .setPassword(Md5Crypt.md5Crypt(dto.getPassword().getBytes()))
                        .setUserName(dto.getUserName())
                        .setOpenId(dto.getOpenId())
                        .setAvatarUrl(dto.getAvatarUrl());
                sysUserDO.setGuid(IdWorker.getId());

                baseMapper.insert(sysUserDO);
                String enc = AES.encrypt(sysUserDO.getGuid() + "");
                expiringMap.remove(dto.getInvitationCode());
                return enc;
            } catch (Exception e) {
                transactionStatus.setRollbackOnly();
                throw new BizException(e);
            }
        });

    }

    @Override
    @Transactional
    public Boolean modifySysUser(SysUserDTO dto) {
        SysUserDO db = baseMapper.selectOne(new LambdaQueryWrapper<SysUserDO>().eq(SysUserDO::getGuid, dto.getGuid()));
        if (db == null) {
            throw new IllegalArgumentException("数据不存在");
        }
        SysUserDO sysUserDO = new SysUserDO()
                .setRole(dto.getRole())
                .setOpenId(dto.getOpenId())
                .setOrganizationGuid(dto.getOrganizationGuid())
                .setAvatarUrl(dto.getAvatarUrl());
        if (StringUtils.isNotBlank(dto.getPassword())) {
            sysUserDO.setPassword(Md5Crypt.md5Crypt(dto.getPassword().getBytes()));
        }
        sysUserDO.setGuid(db.getGuid());
        return 1 == baseMapper.updateById(sysUserDO);
    }

    @Override
    @Transactional
    public Boolean delSysUser(String userName, String guid) {
        return 1 == baseMapper.delete(
                new LambdaQueryWrapper<SysUserDO>()
                        .eq(StringUtils.isNoneBlank(userName), SysUserDO::getUserName, userName)
                        .eq(StringUtils.isNoneBlank(guid), BaseDomain::getGuid, guid)
                        .eq(StringUtils.isBlank(userName) && StringUtils.isBlank(guid), BaseDomain::getGuid, -1)
        );
    }

    @Override
    public Integer getNumber(Long organizationGuid) {
        synchronized (String.valueOf(organizationGuid)) {
            SysUserDO sysUserDO = baseMapper.selectOne(new LambdaQueryWrapper<SysUserDO>().eq(SysUserDO::getOrganizationGuid, organizationGuid).orderByDesc(SysUserDO::getUserName).last("limit 1"));
            return Optional.ofNullable(sysUserDO).map(SysUserDO::getNumber).filter(StringUtils::isNoneBlank).map(e ->
                    Integer.parseInt(e.split("-")[e.split("-").length - 1]) + 1
            ).orElse(1);
        }
    }
}
